﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="HashMap.Enumerator.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Defines the HashMap type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.Collections
{
    using System;
    using System.Collections;
    using System.Collections.Generic;

    public partial class HashMap<TKey, TValue>
    {
        #region Enumerator

        public class Enumerator : IEnumerator<Entry>
        {
            #region Fields

            private readonly HashMap<TKey, TValue> _map;
            private Entry _currEntry;
            private Entry _prevEntry;
            private int _bucketIndex = -1;
            private int _version;

            #endregion

            public Enumerator(HashMap<TKey, TValue> map)
            {
                _map = map;
            }

            private void VerifyCurrent()
            {
                if (_bucketIndex == -1)
                {
                    throw CollectionExceptions.EnumeratorNotStarted();
                }

                if (_bucketIndex >= _map._buckets.Length)
                {
                    throw CollectionExceptions.EnumeratorFinished();
                }

                if (_currEntry == null)
                {
                    throw CollectionExceptions.EnumeratorCurrentRemoved();
                }
            }

            private void VerifyVersion()
            {
                if (_map._version != _version)
                {
                    throw CollectionExceptions.EnumeratorModified();
                }
            }

            public bool CurrentEntryMoveNext()
            {
                if (_currEntry != null)
                {
                    _prevEntry = _currEntry;
                    _currEntry = _currEntry.Next;
                    if (_currEntry != null)
                    {
                        return true;
                    }

                    _prevEntry = null;
                }
                return false;
            }

            private bool PreviousEntryMoveNext()
            {
                if (_prevEntry != null)
                {
                    _currEntry = _prevEntry.Next;
                    if (_currEntry != null)
                    {
                        return true;
                    }

                    // clear prev to indicate new index.
                    _prevEntry = null;
                }
                return false;
            }

            public Entry Current
            {
                get
                {
                    VerifyCurrent();
                    return _currEntry;
                }
            }

            #region IEnumerator Members

            public void Reset()
            {
                _bucketIndex = -1;
                _currEntry = null;
                _prevEntry = null;
            }

            object IEnumerator.Current
            {
                get { return Current; }
            }

            public bool MoveNext()
            {
                if (_bucketIndex == -1)
                {
                    _version = _map._version;
                }
                else
                {
                    VerifyVersion();
                }

                // try Current.Next
                if (CurrentEntryMoveNext())
                {
                    return true;
                }

                // try Previous.Next, for situations where RemoveCurrent() has been called.
                if (PreviousEntryMoveNext())
                {
                    return true;
                }

                // locate next valid table index  
                while ((_currEntry == null) && (++_bucketIndex < _map._buckets.Length))
                {
                    _currEntry = _map._buckets[_bucketIndex];
                }

                return _currEntry != null;
            }

            #endregion

            public void RemoveCurrent()
            {
                VerifyCurrent();
                VerifyVersion();

                if (_currEntry != null)
                {
                    _map.OnRemove(_currEntry);

                    if (_prevEntry != null)
                    {
                        _prevEntry.Next = _currEntry.Next;
                    }
                    else
                    {
                        _map._buckets[_bucketIndex] = _currEntry.Next;
                        if (_currEntry.Next != null)
                        {
                            _bucketIndex--;
                        }
                    }
                    _currEntry.Next = null;

                    _map.Count--;
                    _map._version++;
                    _version++;
                    _currEntry = null;

                    _map.OnRemoveComplete(_currEntry);
                }
            }

            /// <summary>
            /// Has no affect.
            /// </summary>
            /// <remarks>
            /// Do nothing, there are no resources to release. This feels wrong as it leaves the
            /// enumerator accessible, however, it is consistent with <see cref="Dictionary{TKey, TValue}"/>
            /// </remarks>
            void IDisposable.Dispose() { }
        }

        #endregion
    }
}
